package dst.ass1.jpa.model.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

import org.hibernate.annotations.Index;

import dst.ass1.jpa.model.IMembership;
import dst.ass1.jpa.model.ITask;
import dst.ass1.jpa.model.IUser;

@Entity
@NamedQueries({
	  @NamedQuery(
	  //1.2.a.
	  //Find all users who have an active membership for a specific platform and assigned at least x
	  //tasks to this platform (given the name of the platform and the number of tasks).
	  name="usersWithActiveMembership",
	  query = "select DISTINCT u " +
                "from User u " +
                " join u.memberships m " +
                " join u.tasks t " +
                " join t.taskProcessing.taskWorkers w " +  //Task -> TaskProcessing -> TaskWorkersss
                "where m.workPlatform.name = :name " +   //Name of Platform
                " and w.taskForce.workPlatform = m.workPlatform " +  //TaskWorker -> TaskForce -> WorkPlatform
                "group by u " +
                "having count(t) >= :minNr"),   //Number of Tasks
	    //1.2.a.
        //Find the most active user(s), i.e., the user(s) who assigned the largest amount of tasks.       
        @NamedQuery(
	    name="mostActiveUser",
	    query="SELECT DISTINCT u FROM User u " +
	        "JOIN u.tasks t " +
	        "GROUP BY u HAVING COUNT(t) = (" +		//show the users that have greater or equals task then all the others
	            "SELECT MAX(s.tasks.size) FROM User s" +     //select maximum tasks count from all users
	        ")"
	  )
	})
@Table(uniqueConstraints={@UniqueConstraint(columnNames = { "accountNo", "bankCode" })})		//Constraint 1.1.b
public class User extends Person implements IUser{

	private final static Logger LOGGER = Logger.getLogger(TaskProcessing.class.getName());
	
	@Column(unique = true, nullable = false)	//Constraint 1.1.b
	private String username;
	
	@Column(columnDefinition = "binary(16)")		//stored as a cryptographic hash - never store plain text passwords
    @Index(name = "password_ix")					//index with name password_ix
	private byte[] password;
	
	private String accountNo;
	
	private String bankCode;
	
	@OneToMany(targetEntity = Task.class, mappedBy = "user", cascade = CascadeType.ALL,fetch=FetchType.LAZY)  //to-star(collection) props are loaded lazily (as the number of items may well be very large
	private List<ITask> tasks;
	
	@OneToMany(targetEntity = Membership.class, cascade={CascadeType.PERSIST, CascadeType.ALL}, mappedBy = "user",fetch=FetchType.LAZY)  //to-star(collection) props are loaded lazily (as the number of items may well be very large
	private List<IMembership> memberships;
	
	public User (){
		this.memberships= new ArrayList<IMembership>();
		this.tasks= new ArrayList<ITask>();
	}
	
	@Override
	public String getUsername() {
		return username;
	}
	
	@Override
	public void setUsername(String username) {
		this.username = username;
	}
	
	@Override
	public byte[] getPassword() {
		return password;
	}
	
	@Override
	public void setPassword(byte[] password) {
		this.password = password;
	}
	
	@Override
	public String getAccountNo() {
		return accountNo;
	}
	
	@Override
	public void setAccountNo(String accountNo) {
		this.accountNo = accountNo;
	}
	
	@Override
	public String getBankCode() {
		return bankCode;
	}
	
	@Override
	public void setBankCode(String bankCode) {
		this.bankCode = bankCode;
	}
	
	@Override
	public List<ITask> getTasks() {
		return tasks;
	}
	
	@Override
	public void setTasks(List<ITask> tasks) {
		this.tasks = tasks;
	}
	
	@Override
	public List<IMembership> getMemberships() {
		return memberships;
	}
	
	@Override
	public void setMemberships(List<IMembership> memberships) {
		this.memberships = memberships;
	}
	
	@Override
	public void addTask(ITask task) {
		if(this.tasks!=null){
			this.tasks.add(task);
		}
		else{
			LOGGER.info("User.List<ITask> was null");
		}
	}
	
	@Override
	public void addMembership(IMembership membership) {
		if(this.memberships!=null){
			this.memberships.add(membership);
		}
		else{
			LOGGER.info("User.List<IMembership> was null");
		}
	}
	
	

}
